package main

import (
	"context"
	"fmt"
	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"time"
)

//监听etcd中的k v变化，常用做集群中配置下发，状态同步。
func main() {

	//runtime.GOMAXPROCS(2)
	var (
		config      clientv3.Config
		client      *clientv3.Client
		err         error
		getResponse *clientv3.GetResponse
		putResponse *clientv3.PutResponse
		delResponse *clientv3.DeleteResponse

		watchConfirmKeyOnStartRevision int64

		watchCans             clientv3.WatchChan
		watchResponseFromChan clientv3.WatchResponse

		kv clientv3.KV
	)

	config = clientv3.Config{
		Endpoints:   []string{"10.70.30.73:2379"},
		DialTimeout: 3 * time.Second,
	}

	if client, err = clientv3.New(config); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("hello,etcd  ")
	fmt.Println("=============")
	kv = clientv3.NewKV(client)

	// 模拟etcd key的变化

	// 这里有个问题，在这个协程里，如何 在主协程里拿到put的key，看运气？？
	go func() {
		for {
			putResponse, err = kv.Put(context.TODO(), "/cron/job/10", "10")
			fmt.Println("put的revision是: ", putResponse.Header.Revision)
			putResponse, err = kv.Put(context.TODO(), "/cron/job/10", "99")
			fmt.Println("第二次put的revision是: ", putResponse.Header.Revision)
			time.Sleep(1 * time.Microsecond)
			delResponse, err = kv.Delete(context.TODO(), "/cron/job/10")
			fmt.Println("del 的revision  是: ", delResponse.Header.Revision)
			time.Sleep(1 * time.Second)

		}
	}()

	if getResponse, err = kv.Get(context.TODO(), "/cron/job/10"); err != nil {
		fmt.Println(err)
		return
	}

	// 拿到值后再开始监听
	if len(getResponse.Kvs) != 0 {
		fmt.Println("GET当前值为：", string(getResponse.Kvs[0].Value))
		fmt.Println("GET的revision为：", (getResponse.Header.Revision))
		//监听当前key并且revision+=1，因为revision为集群全局的，我们只需要
		watchConfirmKeyOnStartRevision = getResponse.Header.Revision + 1
		fmt.Println("准备监听上个版本加1，版本号为:", watchConfirmKeyOnStartRevision)

	} else {
		time.Sleep(1 * time.Microsecond)
	}

	//启动一个watch，返回一个channel。如果想取消监听，还是可以通过context cancel来实现。取消掉后，watchCans channel 会被close，同样for 循环watchResponseFromChan就会退出，

	//5秒后关闭监听例子
	ctx, cancelFunc := context.WithCancel(context.TODO())
	//下面表示5秒后调用cancelFunc，即cancelFunc(),也就是取消了context
	time.AfterFunc(5*time.Second, func() {
		cancelFunc()
	})
	watchCans = client.Watch(ctx, "/cron/job/10", clientv3.WithRev(watchConfirmKeyOnStartRevision))

	//正常的监听
	//watchCans = client.Watch(context.TODO(), "/cron/job/10", clientv3.WithRev(watchConfirmKeyOnStartRevision))

	//channel中有kv变化的时候，会将response投递到channel中 ，遍历即可
	for watchResponseFromChan = range watchCans {
		//events 是mvccpb.Event数组，事件类型为put 和delete，包括put为新的kv，以及覆盖掉的preKV。这里可以再回头看看createRevision,ModRevision相关。如果是删除操作，此时create_revision归0，后续再创建，create_version 为mod_creation+1。注意这个events可能有其他key的变化，取决你watch的参数

		for _, ev := range watchResponseFromChan.Events {
			//解析方法1
			//	fmt.Printf("===开始监听,事件类型为:%s。 key为%q : value为%q,创建revision为%d,修改revision为%d\n", ev.Type, ev.Kv.Key, ev.Kv.Value, ev.Kv.CreateRevision, ev.Kv.ModRevision)

			//	解析event方法2
			switch ev.Type {
			case mvccpb.PUT:
				fmt.Printf("=====%s事件,key为%q,value为%q,创建Create revision为%d,修改Mod revision为%d\n", ev.Type, ev.Kv.Key, ev.Kv.Value, ev.Kv.CreateRevision, ev.Kv.ModRevision)

			case mvccpb.DELETE:
				fmt.Printf("=====%s事件,key为%q,value为%q,[注意]创建Create revision为%d,修改 Mod revision为%d\n", ev.Type, ev.Kv.Key, ev.Kv.Value, ev.Kv.CreateRevision, ev.Kv.ModRevision)
			}
		}
	}

	//5秒后取消监听结果如下，解析方法2为例
	//
	//	GOROOT=D:\Go #gosetup
	//	GOPATH=C:\Users\yaxia\go #gosetup
	//D:\Go\bin\go.exe build -o C:\Users\yaxia\AppData\Local\Temp\___go_build_GoCrontab_010etcdWatch.exe GoCrontab/010etcdWatch #gosetup
	//C:\Users\yaxia\AppData\Local\Temp\___go_build_GoCrontab_010etcdWatch.exe #gosetup
	//	hello,etcd
	//	=============
	//put的revision是:  311
	//第二次put的revision是:  312
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为311,修改Mod revision为312
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为313
	//	del 的revision  是:  313
	//put的revision是:  314
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为314,修改Mod revision为314
	//第二次put的revision是:  315
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为314,修改Mod revision为315
	//	del 的revision  是:  316
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为316
	//put的revision是:  317
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为317,修改Mod revision为317
	//第二次put的revision是:  318
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为317,修改Mod revision为318
	//	del 的revision  是:  319
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为319
	//put的revision是:  320
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为320,修改Mod revision为320
	//第二次put的revision是:  321
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为320,修改Mod revision为321
	//	del 的revision  是:  322
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为322
	//put的revision是:  323
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为323,修改Mod revision为323
	//第二次put的revision是:  324
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为323,修改Mod revision为324
	//	del 的revision  是:  325
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为325
	//
	//	Process finished with exit code 0

	//不限制监听时间 输出结果如下，解析方法2为例
	//	GOROOT=D:\Go #gosetup
	//	GOPATH=C:\Users\yaxia\go #gosetup
	//D:\Go\bin\go.exe build -o C:\Users\yaxia\AppData\Local\Temp\___go_build_GoCrontab_010etcdWatch.exe GoCrontab/010etcdWatch #gosetup
	//C:\Users\yaxia\AppData\Local\Temp\___go_build_GoCrontab_010etcdWatch.exe #gosetup
	//	hello,etcd
	//	=============
	//put的revision是:  326
	//第二次put的revision是:  327
	//	GET当前值为： 99
	//	GET的revision为： 327
	//	准备监听上个版本加1，版本号为: 328
	//	del 的revision  是:  328
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为328
	//put的revision是:  329
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为329,修改Mod revision为329
	//第二次put的revision是:  330
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为329,修改Mod revision为330
	//	del 的revision  是:  331
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为331
	//put的revision是:  332
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为332,修改Mod revision为332
	//第二次put的revision是:  333
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为332,修改Mod revision为333
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为334
	//	del 的revision  是:  334
	//put的revision是:  335
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为335,修改Mod revision为335
	//第二次put的revision是:  336
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为335,修改Mod revision为336
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为337
	//	del 的revision  是:  337
	//put的revision是:  338
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为338,修改Mod revision为338
	//第二次put的revision是:  339
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为338,修改Mod revision为339
	//	del 的revision  是:  340
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为340
	//put的revision是:  341
	//	=====PUT事件,key为"/cron/job/10",value为"10",创建Create revision为341,修改Mod revision为341
	//第二次put的revision是:  342
	//	=====PUT事件,key为"/cron/job/10",value为"99",创建Create revision为341,修改Mod revision为342
	//	del 的revision  是:  343
	//	=====DELETE事件,key为"/cron/job/10",value为"",[注意]创建Create revision为0,修改 Mod revision为343

}
